﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.IO;
using System.Xml;
using DARE.Properties;
using Microsoft.Xna.Framework;

namespace DARE
{
    /// <summary>
    /// It represents the class that manages the hud skin.
    /// This will automatically load the default skin (already in the dll) except if you specify the skin file that you want to load.
    /// </summary>
    /// <example>
    /// Creation of the HudSystem with you skin.
    /// <code>
    /// HUD = new CHUDSystem("hud", "mySkinFilePath");
    /// m_scene.RegisterRenderer(HUD);
    /// </code>
    /// Usage of the skinner with this xml:
    ///     <Widget name="DefaultWidget">
    ///        <Textures>
    ///          <Body assetName="DefaultSkin/Skin_Blank"/>
    ///          <Left assetName="DefaultSkin/Skin_L_open" Width="5"/>
    ///          <Right assetName="DefaultSkin/Skin_R_open" Width="5"/>
    ///          <Bottom assetName="DefaultSkin/Skin_EM_open" Height="5"/>
    ///          <BottomLeft assetName="DefaultSkin/Skin_EL_open" Width="5" Height="5"/>
    ///          <BottomRight assetName="DefaultSkin/Skin_ER_open" Width="5" Height="5"/>
    ///        </Textures>
    ///        <Button name="ExtendButton">
    ///          <Released>
    ///            <Textures>
    ///              <Body assetName="DefaultSkin/DownArrowGray"/>
    ///            </Textures>
    ///          </Released>
    ///          <Clicked>
    ///            <Textures>
    ///              <Body assetName="DefaultSkin/DownArrow"/>
    ///            </Textures>
    ///          </Clicked>
    ///        </Button>
    ///     </Widget>
    /// <code>
    /// CHudTexture buttonReleasedTexture = HUD.Skinner.GetTexture("DefaultWidgetExtendButtonReleased");
    /// CHudTexture texture = HUD.Skinner.GetTexture("DefaultWidget");
    /// </code>
    /// </example>
    public class CSkinner
    {
        #region fields

        private bool m_isLoaded;
        private Dictionary<string, Texture2D> m_textures;
        private Dictionary<string, CHudTexture> m_cursorTextures;
        private Dictionary<string, SpriteFont> m_spriteFonts;

        private Dictionary<string, CHudTexture> m_hudTextures;

        private SpriteBatch m_spriteBatch;

        #endregion
        
        #region properties

        public bool IsLoaded { get { return m_isLoaded; } }
        public SpriteBatch SpriteBatch { get { return m_spriteBatch; } set { m_spriteBatch = value; } }

        #endregion

        #region ctor

        public CSkinner()
        {
            m_isLoaded = false;
            m_textures = new Dictionary<string, Texture2D>();
            m_spriteFonts = new Dictionary<string, SpriteFont>();
            m_hudTextures = new Dictionary<string, CHudTexture>();
            m_cursorTextures = new Dictionary<string, CHudTexture>();
        }

        #endregion

        #region methods

        public Dictionary<string, CHudTexture> GetCursorTextures()
        {
            return m_cursorTextures;
        }

        /// <summary>
        /// Gets a texture registered under the name given in parameters
        /// </summary>
        public CHudTexture GetTexture(string name)
        {
            if (!m_hudTextures.ContainsKey(name))
            {
                CHudTexture tex = new CHudTexture();
                return tex;
            }
            return m_hudTextures[name].Clone();
        }

        public SpriteFont GetSpriteFont(string name)
        {
            if (!m_spriteFonts.ContainsKey(name))
                return null;
            return m_spriteFonts[name];
        }

        /// <summary>
        /// This method loads a skin from a file. Works only on Windows!
        /// </summary>
        /// <param name="skinFile">the name of the file where the xml is</param>
        /// <returns>true if the loading is ok</returns>
        public bool LoadSkin(string skinFile)
        {
#if WINDOWS
            StreamReader sr = new StreamReader(skinFile);

            XmlDocument xdoc = new XmlDocument();
            xdoc.LoadXml(sr.ReadToEnd());
            foreach (XmlNode node in xdoc.GetElementsByTagName("widget"))
                ParseXmlElement("", node);
            foreach (XmlNode node in xdoc.GetElementsByTagName("cursor"))
                ParseXmlCursor("", node);
            m_isLoaded = true;
#else
#endif
            return m_isLoaded;
        }

        /// <summary>
        /// Loads the default skin (situated in the dll). Works only on Windows!
        /// </summary>
        /// <returns></returns>
        public bool LoadDefaultSkin()
        {
#if WINDOWS
            XmlDocument xdoc = new XmlDocument();
            xdoc.LoadXml(Resources.DefaultSkin);
            foreach (XmlNode node in xdoc.GetElementsByTagName("widget"))
                ParseXmlElement("", node);
            foreach (XmlNode node in xdoc.GetElementsByTagName("cursor"))
                ParseXmlCursor("", node);
#else
#endif
            return true;
        }

        #endregion
#if WINDOWS
        #region parsing

        private void ParseXmlTexturesNode(string name, XmlNode node)
        {
            CHudTexture hudTexture = new CHudTexture();
            hudTexture.SpriteBatch = m_spriteBatch;

            foreach (XmlNode child in node.ChildNodes)
            {
                if (child.NodeType == XmlNodeType.Comment)
                    continue;
                string assetName = "";
                if (child.Attributes.GetNamedItem("assetname") != null)
                    assetName = child.Attributes["assetname"].Value;
                System.Reflection.PropertyInfo pinfo = hudTexture.GetType().GetProperty(child.Name);
                if (node.Attributes["name"] != null)
                    name += node.Attributes["name"].Value;
                if (pinfo != null)
                {
                    if (child.Name.ToLower() == "body")
                    {
                        if (assetName != "")
                            pinfo.SetValue(hudTexture, CResourceMgr.Load<Texture2D>(assetName), null);
                        else
                        {
                            float r = 0;
                            float g = 0;
                            float b = 0;
                            float a = 1;
                            if (child.Attributes["r"] != null)
                                r = float.Parse(child.Attributes["r"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            if (child.Attributes["g"] != null)
                                g = float.Parse(child.Attributes["g"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            if (child.Attributes["b"] != null)
                                b = float.Parse(child.Attributes["b"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            if (child.Attributes["a"] != null)
                                a = float.Parse(child.Attributes["a"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            Color[] c = new Color[1] { new Color(r, g, b, a) };
                            Texture2D t = new Texture2D(DARE.CDare.Instance.GraphicsDevice, 1, 1);
                            t.SetData(c);
                            pinfo.SetValue(hudTexture, t, null);
                        }
                        int width, height;
                        width = height = 0;
                        if (child.Attributes["width"] != null)
                            width = int.Parse(child.Attributes["width"].Value);
                        if (child.Attributes["height"] != null)
                            height = int.Parse(child.Attributes["height"].Value);
                        if (child.Attributes["timelaps"] != null)
                            hudTexture.AnimationState.AnimationInterval = float.Parse(child.Attributes["timelaps"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                        if (child.Attributes["type"] != null)
                            switch (child.Attributes["type"].Value.ToLower())
                            {
                                case "loop":
                                    hudTexture.AnimationState.AnimationType = CAnimationState.EAnimationType.LOOP;
                                    break;
                                case "handle":
                                    hudTexture.AnimationState.AnimationType = CAnimationState.EAnimationType.HANDLE;
                                    break;
                                case "pingpong":
                                    hudTexture.AnimationState.AnimationType = CAnimationState.EAnimationType.PINGPONG;
                                    break;
                            }
                        if (child.Attributes["defaultstate"] != null)
                            hudTexture.AnimationState.DefaultState = int.Parse(child.Attributes["defaultstate"].Value);
                        hudTexture.Rectangle = new Microsoft.Xna.Framework.Rectangle(0, 0, width, height);
                    }
                    else if (child.Name.ToLower() == "bodyanimationstate")
                    {
                        int width, height, x, y;
                        width = height = x = y = 0;
                        if (child.Attributes["x"] != null)
                            x = int.Parse(child.Attributes["x"].Value);
                        if (child.Attributes["y"] != null)
                            y = int.Parse(child.Attributes["y"].Value);
                        if (child.Attributes["width"] != null)
                            width = int.Parse(child.Attributes["width"].Value);
                        else if (hudTexture.Body != null)
                            width = hudTexture.Body.Width;
                        if (child.Attributes["height"] != null)
                            height = int.Parse(child.Attributes["height"].Value);
                        else if (hudTexture.Body != null)
                            height = hudTexture.Body.Height;
                        Microsoft.Xna.Framework.Rectangle r = new Microsoft.Xna.Framework.Rectangle(x, y, width, height);
                        pinfo.SetValue(hudTexture, r, null);
                    }
                    else
                    {
                        int x, y, width, height;
                        x = y = width = height = 0;
                        if (child.Attributes["width"] != null)
                            width = int.Parse(child.Attributes["width"].Value);
                        if (child.Attributes["height"] != null)
                            height = int.Parse(child.Attributes["height"].Value);
                        Microsoft.Xna.Framework.Rectangle rec = new Microsoft.Xna.Framework.Rectangle(x, y, width, height);
                        Texture2D t;
                        if (assetName != "")
                            t = CResourceMgr.Load<Texture2D>(assetName);
                        else
                        {
                            float r = 0;
                            float g = 0;
                            float b = 0;
                            float a = 1;
                            if (child.Attributes["r"] != null)
                                r = float.Parse(child.Attributes["r"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            if (child.Attributes["g"] != null)
                                g = float.Parse(child.Attributes["g"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            if (child.Attributes["b"] != null)
                                b = float.Parse(child.Attributes["b"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            if (child.Attributes["a"] != null)
                                a = float.Parse(child.Attributes["a"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                            Color[] c = new Color[1] { new Color(r, g, b, a) };
                            t = new Texture2D(DARE.CDare.Instance.GraphicsDevice, 1, 1);
                            t.SetData(c);
                        }
                        pinfo.SetValue(hudTexture, new KeyValuePair<Texture2D, Microsoft.Xna.Framework.Rectangle>(t, rec), null);
                    }
                }
            }
            if (m_hudTextures.ContainsKey(name))
                m_hudTextures.Remove(name);
            m_hudTextures.Add(name, hudTexture);
        }

        private void ParseXmlSpriteFontsNode(string name, XmlNode node)
        {
            foreach (XmlNode child in node.ChildNodes)
                if (child.Attributes["assetname"] != null)
                    m_spriteFonts.Add(name + child.Name, CResourceMgr.Load<SpriteFont>(child.Attributes["assetname"].Value));
        }

        private void ParseXmlCursorTexturesNode(string name, XmlNode node)
        {
            CHudTexture cursorTexture = new CHudTexture();
            cursorTexture.SpriteBatch = m_spriteBatch;

            foreach (XmlNode child in node.ChildNodes)
            {
                if (child.NodeType == XmlNodeType.Comment)
                    continue;
                string assetName = "";
                if (child.Attributes.GetNamedItem("assetname") != null)
                    assetName = child.Attributes["assetname"].Value;
                System.Reflection.PropertyInfo pinfo = cursorTexture.GetType().GetProperty(child.Name);
                if (node.Attributes["name"] != null)
                    name += node.Attributes["name"].Value;
                if (pinfo != null)
                {
                    if (child.Name.ToLower() == "body")
                    {
                        pinfo.SetValue(cursorTexture, CResourceMgr.Load<Texture2D>(assetName), null);
                        int width, height;
                        width = height = 0;
                        if (child.Attributes["width"] != null)
                            width = int.Parse(child.Attributes["width"].Value);
                        if (child.Attributes["height"] != null)
                            height = int.Parse(child.Attributes["height"].Value);
                        if (child.Attributes["timelaps"] != null)
                            cursorTexture.AnimationState.AnimationInterval = float.Parse(child.Attributes["timelaps"].Value, CDare.Instance.Config.General.NumberFormatInfo);
                        if (child.Attributes["type"] != null)
                            switch (child.Attributes["type"].Value.ToLower())
                            {
                                case "loop":
                                    cursorTexture.AnimationState.AnimationType = CAnimationState.EAnimationType.LOOP;
                                    break;
                                case "handle":
                                    cursorTexture.AnimationState.AnimationType = CAnimationState.EAnimationType.HANDLE;
                                    break;
                                case "pingpong":
                                    cursorTexture.AnimationState.AnimationType = CAnimationState.EAnimationType.PINGPONG;
                                    break;
                            }
                        if (child.Attributes["defaultstate"] != null)
                            cursorTexture.AnimationState.DefaultState = int.Parse(child.Attributes["defaultstate"].Value);
                        cursorTexture.Rectangle = new Microsoft.Xna.Framework.Rectangle(0, 0, width, height);
                    }
                    else if (child.Name.ToLower() == "bodyanimationstate")
                    {
                        int width, height, x, y;
                        width = height = x = y = 0;
                        if (child.Attributes["x"] != null)
                            x = int.Parse(child.Attributes["x"].Value);
                        if (child.Attributes["y"] != null)
                            y = int.Parse(child.Attributes["y"].Value);
                        if (child.Attributes["width"] != null)
                            width = int.Parse(child.Attributes["width"].Value);
                        else if (cursorTexture != null)
                            width = cursorTexture.Body.Width;
                        if (child.Attributes["height"] != null)
                            height = int.Parse(child.Attributes["height"].Value);
                        else if (cursorTexture.Body != null)
                            height = cursorTexture.Body.Height;
                        Microsoft.Xna.Framework.Rectangle r = new Microsoft.Xna.Framework.Rectangle(x, y, width, height);
                        pinfo.SetValue(cursorTexture, r, null);
                    }
                    else
                    {
                        int x, y, width, height;
                        x = y = width = height = 0;
                        if (child.Attributes["width"] != null)
                            width = int.Parse(child.Attributes["width"].Value);
                        if (child.Attributes["height"] != null)
                            height = int.Parse(child.Attributes["height"].Value);
                        Microsoft.Xna.Framework.Rectangle r = new Microsoft.Xna.Framework.Rectangle(x, y, width, height);
                        Texture2D t = CResourceMgr.Load<Texture2D>(assetName);
                        pinfo.SetValue(cursorTexture, new KeyValuePair<Texture2D, Microsoft.Xna.Framework.Rectangle>(t, r), null);
                    }
                }
            }
            if (m_cursorTextures.ContainsKey(name))
                m_cursorTextures.Remove(name);
            m_cursorTextures.Add(name, cursorTexture);
        }

        private void ParseXmlCursor(string name, XmlNode node)
        {
            string n;
            if (node.Attributes.Count > 0)
                n = name + node.Attributes[0].Value;
            else
                n = name + node.Name;
            foreach (XmlNode child in node.ChildNodes)
            {
                switch (child.Name)
                {
                    case "textures":
                        ParseXmlCursorTexturesNode(n, child);
                        break;
                    default:
                        ParseXmlCursor(n, child);
                        break;
                }
            }
        }

        private void ParseXmlElement(string name, XmlNode node)
        {
            string n;
            if (node.Attributes.Count > 0)
                n = name + node.Attributes[0].Value;
            else
                n = name + node.Name;
            foreach (XmlNode child in node.ChildNodes)
            {
                switch (child.Name)
                {
                    case "textures":
                        ParseXmlTexturesNode(n, child);
                        break;
                    case "spritefonts":
                        ParseXmlSpriteFontsNode(n, child);
                        break;
                    default:
                        ParseXmlElement(n, child);
                        break;
                }
            }
        }

        #endregion
#endif
    }
}
